package me.pinion.service;

import me.pinion.Log;
import me.pinion.exception.BookExistException;
import me.pinion.kit.DateKit;
import me.pinion.model.Visit;
import me.pinion.model.book.Book;
import me.pinion.model.ModelType;
import me.pinion.model.Tag;
import me.pinion.model.User;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BookService extends Service {

    ChapterService chapterService = ChapterService.getInstance();

    private static BookService bookService;

    public static BookService getInstance(){
        if (bookService == null){
            bookService = new BookService();
        }
        return bookService;
    }

    public List<Book> my(User user) {
        return user.getOwnBooks();
    }

    public List<Book> favorite(User user) {
        return user.getFavoBooks();
    }

    public List<Book> like(User user) {
        return user.getLikeBooks();
    }

    public Book create(String bookname, String description, User user) throws BookExistException {
        Book book = Book.dao.findByBookname(bookname);
        if (book != null && !book.getAuthor().equals(user)) {
            throw new BookExistException();
        }
        if (book != null) {
            book.set("description", description);
            book.update();
        } else {
            book = new Book();
            book.set("bookname", bookname);
            book.set("description", description);
            book.set("author_id", user.getId());
            book.save();
        }
        Log.i("[ :BOOK: ] => " + bookname);
        Visit.dao.visit(book, user);
        return book;
    }

    public List<Book> search(String q) {
        return Book.dao.paginate(
                1,
                10,
                "SELECT b.*, count(b.id) as 'visit_count' ",
                "FROM book as b, x_visit AS v " +
                        "WHERE v.target_id=b.id " +
                        "AND b.bookname LIKE ? " +
                        "AND v.target_type=? " +
                        "AND b.enabled=true " +
                        "GROUP BY b.id " +
                        "ORDER BY count(v.id) DESC",
                "%" + q + "%",
                ModelType.dao.getByClass(Book.class).getId()).getList();
    }

    public List<String> searchBookName(String q) {
        List<Book> books = Book.dao.paginate(
                1,
                10,
                "SELECT b.bookname, count(b.id) as 'visit_count' ",
                "FROM book as b, x_visit AS v " +
                        "WHERE v.target_id=b.id " +
                        "AND b.bookname LIKE ? " +
                        "AND v.target_type=? " +
                        "AND b.enabled=true " +
                        "GROUP BY b.id " +
                        "ORDER BY count(v.id) DESC",
                "%" + q + "%",
                ModelType.dao.getByClass(Book.class).getId()).getList();
        List<String> names = new ArrayList<String>();
        for (Book book : books) {
            names.add(book.getBookname());
        }
        return names;
    }

    public List<Book> popularBooks(int page, int pageSize) {
        return popularBooks("MONTH",page, pageSize);
    }

    public List<Book> popularBooks(String recent,int page, int pageSize) {
        return Book.dao.paginate(page, pageSize, "SELECT b.*, count(b.id) as 'visit_count'",
                "FROM book as b, x_visit AS v " +
                        "WHERE v.target_id=b.id " +
                        "AND v.target_type=? " +
                        "AND b.enabled=true " +
                        "AND v.datetime > ?" +
                        "GROUP BY b.id " +
                        "ORDER BY count(v.id) DESC"
                , ModelType.dao.getByClass(Book.class).getId()
                , DateKit.recentStr2Date(recent)
        ).getList();
    }

    public List<Book> popularBooks(Tag tag, String recent, int page, int pageSize) {
        List<Book> books;
        if (tag == null) {
            books = this.popularBooks(recent, page, pageSize);
        } else {
            books = Book.dao.paginate(page, pageSize, "SELECT \n" +
                    "    b . *, count(b.id) as 'visit_count'\n",
                    "FROM\n" +
                            "    book as b,\n" +
                            "    x_visit AS v\n" +
                            "WHERE\n" +
                            "    b.id in (SELECT \n" +
                            "            b . id\n" +
                            "        FROM\n" +
                            "            book as b,\n" +
                            "            tag_object as tt\n" +
                            "        WHERE\n" +
                            "            tt.tag_id = ? AND tt.target_id = b.id\n" +
                            "                AND tt.target_type_id = ?\n" +
                            "        GROUP BY b.id)\n" +
                            "        AND v.target_id = b.id\n" +
                            "        AND v.target_type = ?\n" +
                            "        AND b.enabled = true\n" +
                            "AND v.datetime > ?" +
                            "GROUP BY b.id\n" +
                            "ORDER BY count(v.id) DESC",
                    tag.getLong("id"),
                    ModelType.dao.getByClass(Book.class).getId(),
                    ModelType.dao.getByClass(Book.class).getId(),
                    DateKit.recentStr2Date(recent)
            ).getList();
        }
        return books;
    }

    public Book loadFile(File file, User user, String charset) throws BookExistException, IOException {
        BufferedReader br = null;
        ChapterSpMatcher spMatcher = new ChapterSpMatcher(file);
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
        } catch (FileNotFoundException e) {
            throw new RuntimeException();
        }
        String line, curCh = null, curTitle = null, description = "", bookname = file.getName().split("\\.")[0];
        StringBuffer curText = new StringBuffer("");
        Book book = null;
        while ((line = br != null ? br.readLine() : null) != null) {
            Matcher matcher = spMatcher.getMatcher(line);
            if (matcher.find(0) && line.length() < 64) {
                if (curCh != null) {
                    if (curText.toString().length() > 255){
                        chapterService.create(curTitle, curText.toString(), book);
                        spMatcher.setPattern();
                    }
                } else {
                    book = this.create(bookname, description.substring(0, description.length() > 512 ? 512 : description.length()), user);
                    if (description.length() > 256){
                        book.set("prologue", description);
                        book.update();
                    }
                }
                curCh = matcher.group();
                curTitle = line;
                curText = new StringBuffer("");
            }
            if (curCh != null) curText.append("<p>" + line + "</p>");
            else description += line + "\n";
        }
        if (curCh != null) chapterService.create(curTitle, curText.toString(), book);
        file.delete();
        return book;
    }

    public Book loadFile(File file) throws BookExistException {
        try {
            return this.loadFile(file, User.dao.findById(1), "UTF-8");
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }

    private class ChapterSpMatcher{
        Pattern[] patterns = new Pattern[]{
                Pattern.compile("第(\\s?[一二三四五六七八九十百千万]+\\s?)章"),
                Pattern.compile("第(\\s?[一二三四五六七八九十百千万]+\\s?)回"),
                Pattern.compile("第(\\s?[一二三四五六七八九十百千万]+\\s?)卷"),
                Pattern.compile("第(\\s?[一二三四五六七八九十百千万]+\\s?)话"),
                Pattern.compile("^(\\s+[一二三四五六七八九十百千万]+\\s+)$"),
                Pattern.compile("第(\\s?\\d+\\s?)章"),
                Pattern.compile("第(\\s?\\d+\\s?)回"),
                Pattern.compile("第(\\s?\\d+\\s?)卷"),
                Pattern.compile("第(\\s?\\d+\\s?)话"),
                Pattern.compile("^(\\s+\\d+\\s+)$")
        };
        Pattern curPattern;
        Pattern reaPattern;
        File file;

        private ChapterSpMatcher(File file) {
            this.file = file;
        }
        public Matcher getMatcher(String line) {

            Matcher matcher = null;
            if (reaPattern == null){
                for (Pattern pattern : patterns) {
                    matcher = pattern.matcher(line);
                    if (matcher.find(0)) {
                        curPattern = pattern;
                        reaPattern = pattern;
                        return matcher;
                    }
                }
            }else {
                matcher = reaPattern.matcher(line);
            }

            return matcher;
        }

        public void setPattern() {
            this.reaPattern = this.curPattern;
        }
    }
}
